跳到主要内容

SpringBoot 全局异常处理

全局异常处理

参考资料 SpringBoot优雅的全局异常处理(大部分抄了这篇文章,但是一些细节例如 Code 类型的更改了)

SpringBoot 的项目已经对有一定的异常处理了,但是返回的格式未必是自定的格式。所以 SpringBoot 中有一个 ControllerAdvice 的注解,使用该注解表示开启了全局异常的捕获,只需在自定义一个方法使用 ExceptionHandler 注解然后定义捕获异常的类型即可对这些捕获的异常进行统一的处理。

编写异常接口

定义一个基础的接口类,自定义的错误描述枚举类需实现该接口

public interface BaseErrorInfoInterface {
/** 错误码*/
Integer getResultCode();

/** 错误描述*/
String getResultMsg();
}

使用枚举管理异常

创建一个枚举去实现这个接口

public enum CommonEnum implements BaseErrorInfoInterface{
// 数据操作错误定义
SUCCESS(200, "成功!"),
CREATED(201, "创建成功!"),
DELETED(204, "删除成功!"),
BAD_REQUEST(400,"请求的数据格式不符!"),
UNAUTHORIZED(401,"请求的数字签名不匹配!"),
FORBIDDEN(403,"被禁止访问!"),
NOT_FOUND(404, "请求的资源不存在!"),
INTERNAL_SERVER_ERROR(500, "服务器内部错误!"),
SERVER_BUSY(503,"服务器正忙,请稍后再试!")
;

/** 响应码 */
private final Integer resultCode;

/** 响应描述 */
private final String resultMsg;

CommonEnum(Integer resultCode, String resultMsg) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}

@Override
public Integer getResultCode() {
return resultCode;
}

@Override
public String getResultMsg() {
return resultMsg;
}
}

定义异常类

然后自定义一个异常类,用于处理发生的业务异常。

public class BizException extends RuntimeException {

private static final long serialVersionUID = 1L;

/**
* 错误码
*/
protected Integer errorCode;
/**
* 错误信息
*/
protected String errorMsg;

public BizException() {
super();
}

public BizException(BaseErrorInfoInterface errorInfoInterface) {
super(errorInfoInterface.getResultMsg());
this.errorCode = errorInfoInterface.getResultCode();
this.errorMsg = errorInfoInterface.getResultMsg();
}

public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {
super(errorInfoInterface.getResultMsg(), cause);
this.errorCode = errorInfoInterface.getResultCode();
this.errorMsg = errorInfoInterface.getResultMsg();
}

public BizException(String errorMsg) {
super(errorMsg);
this.errorMsg = errorMsg;
}

public BizException(Integer errorCode, String errorMsg) {
super(errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}

public BizException(Integer errorCode, String errorMsg, Throwable cause) {
super(errorMsg, cause);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}


public Integer getErrorCode() {
return errorCode;
}

public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}

public String getErrorMsg() {
return errorMsg;
}

public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}

@Override
public String getMessage() {
return errorMsg;
}

@Override
public synchronized Throwable fillInStackTrace() {
return this;
}

}

全局异常处理类

最后加上 自定义全局异常处理类,只需要在类上加上 @ControllerAdvice 注解这个类就成为了全局异常处理类

@ControllerAdvice
@Slf4j
@ResponseBody
public class GlobalExceptionHandler {

/**
* 处理自定义的业务异常
* @param e Spring 会捕获 BizException 异常传入这个方法里
*/
@ExceptionHandler(value = BizException.class)
public ResponseTemplate<JSONObject> bizExceptionHandler(HttpServletRequest req, BizException e){
log.error("发生业务异常!原因是:{}",e.getErrorMsg());
JSONObject result = new JSONObject();
result.put("status",e.getErrorMsg());
return ResponseTemplate.<JSONObject>builder()
.code(e.getErrorCode())
.message(e.getErrorMsg())
.data(result)
.build();
}

/**
* 处理空指针的异常
* @param e Spring 会捕获 BizException 异常传入这个方法里
*/
@ExceptionHandler(value =NullPointerException.class)
@ResponseBody
public ResponseTemplate<JSONObject> exceptionHandler(HttpServletRequest req, NullPointerException e){
log.error("发生空指针异常!原因是: ",e);
JSONObject result = new JSONObject();
result.put("status",CommonEnum.BAD_REQUEST);
return ResponseTemplate.<JSONObject>builder()
.code(CommonEnum.BAD_REQUEST.getResultCode())
.message(CommonEnum.BAD_REQUEST.getResultMsg())
.data(result)
.build();
}


/**
* 处理其他异常
* @param e Spring 会捕获 BizException 异常传入这个方法里
*/
@ExceptionHandler(value =Exception.class)
@ResponseBody
public ResponseTemplate<JSONObject> exceptionHandler(HttpServletRequest req, Exception e){
log.error("未知异常!原因是:",e);
JSONObject result = new JSONObject();
result.put("status",CommonEnum.INTERNAL_SERVER_ERROR);
return ResponseTemplate.<JSONObject>builder()
.code(CommonEnum.INTERNAL_SERVER_ERROR.getResultCode())
.message(CommonEnum.INTERNAL_SERVER_ERROR.getResultMsg())
.data(result)
.build();
}
}

最后使用时直接抛出错误就行了

throw new BizException(CommonEnum.INTERNAL_SERVER_ERROR, error);

添加公共包的全局异常处理

参考资料 Spring Boot多模块项目中,解决全局异常捕获不生效的问题

最近项目开始使用分模块的方式开发,将公共代码提取出来,单独放到一个模块中。

如上图所示把异常处理单独放在一个公共包里面

package com.alsritter.changgou_common.exception;

@ControllerAdvice
public class BaseExceptionHandler {

@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result error(Exception e) {
e.printStackTrace();
return new Result(false, StatusCode.ERROR, e.getMessage());
}
}

其中全局异常捕获相关的代码也丢到了common 中。 但是依赖 common 模块的 SpringBoot 子项目应用启动后,全局异常捕获并没有生效。

那如何在子项目中使用到这个公共的异常处理呢?

只需在子项目启动类里面加上这个公共包的路径,使之也能被扫描到~

// 别忘了加上自己当前包,否则会变得只扫描公共包...
@SpringBootApplication(scanBasePackages = {"com.alsritter.goodsservice", "com.alsritter.changgou_common"})
@EnableEurekaClient
@MapperScan(basePackages = {"com.alsritter.goodsservice.dao"})
public class GoodsApplication {
public static void main(String[] args) {
SpringApplication.run(GoodsApplication.class);
}
}